package com.ziki.bswicket.panel;

import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.list.LoopItem;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigationIncrementLink;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigationLink;
import org.apache.wicket.markup.html.panel.Panel;

import com.ziki.framework.wicket.behavior.FwAttributeModifier;
import com.ziki.framework.wicket.markup.html.FwContainer;

public class BsPagingNavigator extends Panel {
    private static final long serialVersionUID = 1L;
    private PagingNavigation pagingNavigation;
    private final IPageable pageable;
    private final IPagingLabelProvider labelProvider;
    private FwContainer firstli;
    private FwContainer prevli;
    private FwContainer nextli;
    private FwContainer lastli;

    /**
     * Constructor.
     * 
     * @param id See Component
     * @param pageable The pageable component the page links are referring to.
     */
    public BsPagingNavigator(final String id, final IPageable pageable) {
        this(id, pageable, null);
    }

    /**
     * Constructor.
     * 
     * @param id See Component
     * @param pageable The pageable component the page links are referring to.
     * @param labelProvider The label provider for the link text.
     */
    public BsPagingNavigator(final String id, final IPageable pageable, final IPagingLabelProvider labelProvider) {
        super(id);
        this.pageable = pageable;
        this.labelProvider = labelProvider;
    }

    /**
     * {@link IPageable} this navigator is linked with
     * 
     * @return {@link IPageable} instance
     */
    public final IPageable getPageable() {
        return pageable;
    }

    @Override
    protected void onInitialize() {
        super.onInitialize();

        // Get the navigation bar and add it to the hierarchy
        pagingNavigation = newNavigation("navigation", pageable, labelProvider);
        add(pagingNavigation);

        // Add additional page links
        AbstractLink first = newPagingNavigationLink("first", pageable, 0);
        first.add(new TitleAppender("PagingNavigator.first"));
        firstli = new FwContainer("firstli");
        firstli.add(first);

        AbstractLink prev = newPagingNavigationIncrementLink("prev", pageable, -1);
        prev.add(new TitleAppender("PagingNavigator.previous"));
        prevli = new FwContainer("prevli");
        prevli.add(prev);

        AbstractLink next = newPagingNavigationIncrementLink("next", pageable, 1);
        next.add(new TitleAppender("PagingNavigator.next"));
        nextli = new FwContainer("nextli");
        nextli.add(next);

        AbstractLink last = newPagingNavigationLink("last", pageable, -1);
        last.add(new TitleAppender("PagingNavigator.last"));
        lastli = new FwContainer("lastli");
        lastli.add(last);

        add(firstli);
        add(prevli);
        add(nextli);
        add(lastli);
    }

    /**
     * Create a new increment link. May be subclassed to make use of specialized
     * links, e.g. Ajaxian links.
     * 
     * @param id the link id
     * @param pageable the pageable to control
     * @param increment the increment
     * @return the increment link
     */
    protected AbstractLink newPagingNavigationIncrementLink(String id, IPageable pageable, int increment) {
        return new PagingNavigationIncrementLink<Void>(id, pageable, increment);
    }

    /**
     * Create a new pagenumber link. May be subclassed to make use of
     * specialized links, e.g. Ajaxian links.
     * 
     * @param id the link id
     * @param pageable the pageable to control
     * @param pageNumber the page to jump to
     * @return the pagenumber link
     */
    protected AbstractLink newPagingNavigationLink(String id, IPageable pageable, int pageNumber) {
        return new PagingNavigationLink<Void>(id, pageable, pageNumber);
    }

    /**
     * Create a new PagingNavigation. May be subclassed to make us of
     * specialized PagingNavigation.
     * 
     * @param id The id of the navigation component
     * @param pageable the pageable component
     * @param labelProvider The label provider for the link text.
     * @return the navigation object
     */
    protected PagingNavigation newNavigation(final String id, final IPageable pageable, final IPagingLabelProvider labelProvider) {
        return new BsPagingNavigation(id, pageable, labelProvider);
    }

    /**
     * Gets the pageable navigation component for configuration purposes.
     * 
     * @return the associated pageable navigation.
     */
    public final PagingNavigation getPagingNavigation() {
        return pagingNavigation;
    }

    /**
     * Appends title attribute to navigation links
     * 
     * @author igor.vaynberg
     */
    private final class TitleAppender extends Behavior {
        private static final long serialVersionUID = 1L;

        private final String resourceKey;

        /**
         * Constructor
         * 
         * @param resourceKey resource key of the message
         */
        public TitleAppender(String resourceKey) {
            this.resourceKey = resourceKey;
        }

        /** {@inheritDoc} */
        @Override
        public void onComponentTag(Component component, ComponentTag tag) {
            tag.put("title", BsPagingNavigator.this.getString(resourceKey));
        }
    }

    private class BsPagingNavigation extends PagingNavigation {
        private static final long serialVersionUID = 1L;

        public BsPagingNavigation(String id, IPageable pageable, IPagingLabelProvider labelProvider) {
            super(id, pageable, labelProvider);
        }

        @Override
        protected void populateItem(final LoopItem loopItem) {
            // Get the index of page this link shall point to
            final long pageIndex = getStartIndex() + loopItem.getIndex();

            // Add a page link pointing to the page
            final AbstractLink link = newPagingNavigationLink("pageLink", pageable, pageIndex);
            loopItem.add(link);

            // Add a page number label to the list which is enclosed by the link
            String label = "";
            if (labelProvider != null) {
                label = labelProvider.getPageLabel(pageIndex);
            } else {
                label = String.valueOf(pageIndex + 1);
            }
            link.add(new Label("pageNumber", label));

            if (pageable.getCurrentPage() == pageIndex) {
                loopItem.add(FwAttributeModifier.replace("class", "active"));
            }
        }
    }

    @Override
    protected void onBeforeRender() {
        super.onBeforeRender();
        if (pageable.getCurrentPage() + 1 == 1) {
            firstli.add(FwAttributeModifier.replace("class", "disabled"));
            firstli.setEnabled(false);
            prevli.add(FwAttributeModifier.replace("class", "disabled"));
            prevli.setEnabled(false);
            nextli.add(FwAttributeModifier.remove("class"));
            nextli.setEnabled(true);
            lastli.add(FwAttributeModifier.remove("class"));
            lastli.setEnabled(true);
        } else if (pageable.getCurrentPage() + 1 == pageable.getPageCount()) {
            nextli.add(FwAttributeModifier.replace("class", "disabled"));
            nextli.setEnabled(false);
            lastli.add(FwAttributeModifier.replace("class", "disabled"));
            lastli.setEnabled(false);
            firstli.add(FwAttributeModifier.remove("class"));
            firstli.setEnabled(true);
            prevli.add(FwAttributeModifier.remove("class"));
            prevli.setEnabled(true);
        } else {
            firstli.add(FwAttributeModifier.remove("class"));
            firstli.setEnabled(true);
            prevli.add(FwAttributeModifier.remove("class"));
            prevli.setEnabled(true);
            nextli.add(FwAttributeModifier.remove("class"));
            nextli.setEnabled(true);
            lastli.add(FwAttributeModifier.remove("class"));
            lastli.setEnabled(true);
        }
    }
}
